home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / clisp-li.000 / clisp-li / clisp-1996-07-22 / src / screen.lsp < prev    next >
Encoding:
Lisp/Scheme  |  1995-04-02  |  15.6 KB  |  482 lines

  1. (in-package "SCREEN" :use '("LISP"))
  2.  
  3. #|
  4. ; Re-Export von importierten Symbolen kann man nicht mit der
  5. ; P I S E R U I - Regel erreichen. Diese Symbole mu▀ man zuerst importieren.
  6. (in-package "SYSTEM")
  7. (import '(make-window window-size
  8.           window-cursor-position set-window-cursor-position
  9.           clear-window clear-window-to-eot clear-window-to-eol
  10.           delete-window-line insert-window-line
  11.           highlight-on highlight-off window-cursor-on window-cursor-off
  12.          )
  13.         "SCREEN"
  14. )
  15. (in-package "SCREEN")
  16. |#
  17.  
  18. (export '(; exported functions and macros:
  19.           make-window window-size
  20.           window-cursor-position set-window-cursor-position
  21.           clear-window clear-window-to-eot clear-window-to-eol
  22.           delete-window-line insert-window-line
  23.           highlight-on highlight-off window-cursor-on window-cursor-off
  24.           with-window *window*
  25.           read-keyboard-char
  26.           ; user-settable things:
  27.           *new-window*
  28. )        )
  29.  
  30. (proclaim '(special *window*))
  31.  
  32. #-AMIGA
  33. (defun read-keyboard-char (stream)
  34.   (declare (ignore stream))
  35.   (read-char *keyboard-input*)
  36. )
  37.  
  38. #-AMIGA
  39. (defconstant *new-window* nil)
  40.  
  41.  
  42. ;;;; SCREEN-Package for Amiga
  43. ;;;; J÷rg H÷hle, 17.1.1995
  44.  
  45. #+AMIGA (use-package "CLOS")
  46. #+AMIGA
  47. (progn
  48.  
  49. ; Determines the "new window" policy.
  50. (defvar *new-window* "RAW:0/11/581/231/CLISP Window"
  51.   "This variables determines the behaviour of SCREEN:MAKE-WINDOW.
  52. If NIL, it uses *TERMINAL-IO*. If non-NIL, it should be the specification
  53. string of a special file to be OPENed, e.g. \"RAW:0/11/581/231/Window Title\"."
  54. )
  55.  
  56. ;;; Why is this so complex? Because applications (Punimax) need to use
  57. ;;; the raw mode functions but nevertheless expect to read input in
  58. ;;; cooked mode. Cooked mode is also nicer if you happen to fall into
  59. ;;; the debugger. Thus I provide special streams that switch modes
  60. ;;; automatically.
  61.  
  62.  
  63. ;; The class of all data present in SCREEN's generic streams.
  64. (defclass screen-controller (generic-stream-controller) ())
  65.  
  66. ; Most stream functions are aliased to the following stream:
  67. (defgeneric controller-stream (controller))
  68.  
  69. ; The screen's mode: either T (raw) or NIL (line editing enabled)
  70. (defgeneric controller-mode (controller))
  71. (defgeneric (setf controller-mode) (mode controller))
  72.  
  73.  
  74. ;; Two subclasses:
  75.  
  76. ; terminal-controller generic streams refer to *terminal-io*.
  77. (defclass terminal-controller (screen-controller) ())
  78. (defmethod controller-stream ((controller terminal-controller))
  79.   *terminal-io*
  80. )
  81. ; The terminal's mode is cached in stream.d, no need to cache it here.
  82.  
  83. ; window-controller generic streams refer to a special device stream.
  84. (defclass window-controller (screen-controller)
  85.   ((stream :reader controller-stream
  86.            :type stream
  87.            :initarg :stream
  88.    )
  89.    (mode :accessor controller-mode
  90.          :initform 'unknown ; the initial mode is unknown
  91.   ))
  92. )
  93.  
  94.  
  95. ;; (raw-mode stream mode) puts the stream into the given mode (T or NIL)
  96. ;; and returns the old mode.
  97. (defun raw-mode (stream mode)
  98.   (if (generic-stream-p stream)
  99.     (generic-raw-mode (generic-stream-controller stream) mode)
  100.     ; handle low-level streams here
  101.     (sys::terminal-raw stream mode t)
  102. ) )
  103. (defgeneric generic-raw-mode (controller mode))
  104. (defmethod generic-raw-mode ((controller screen-controller) mode)
  105.   (raw-mode (controller-stream controller) mode)
  106. )
  107. (defmethod generic-raw-mode ((controller window-controller) mode)
  108.   (let ((old-mode (controller-mode controller)))
  109.     ; compare against the cached current mode
  110.     (if (eq mode old-mode)
  111.       old-mode
  112.       (prog1
  113.         (raw-mode (controller-stream controller) mode)
  114.         (setf (controller-mode controller) mode)
  115. ) ) ) )
  116.  
  117.  
  118. ;; Return a new window stream.
  119. (defun make-window (&optional (*new-window* *new-window*))
  120.   (let ((stream
  121.           (make-generic-stream
  122.             (if *new-window*
  123.               (make-instance 'window-controller
  124.                 :stream (etypecase *new-window*
  125.                           (STREAM *new-window*)
  126.                           ((OR PATHNAME STRING) (open *new-window* :direction :io))
  127.               )         )
  128.               (make-instance 'terminal-controller)
  129.        )) ) )
  130.     ; (raw-mode stream t) ; Don't need this because modes are switched automatically.
  131.     stream
  132. ) )
  133.  
  134.  
  135. ;; Operations on SCREEN streams.
  136.  
  137. (defmethod generic-stream-read-char ((controller screen-controller))
  138.   (generic-raw-mode controller nil) ;; need to switch to cooked mode
  139.   (read-char (controller-stream controller))
  140. )
  141.  
  142. (defmethod generic-stream-listen ((controller screen-controller))
  143.   (generic-raw-mode controller nil) ;; need to switch to cooked mode
  144.   (if (listen (controller-stream controller))
  145.     0 ; something available
  146.     +1 ; nothing available, not EOF
  147. ) )
  148.  
  149. (defmethod generic-stream-clear-input ((controller screen-controller))
  150.   (generic-raw-mode controller nil) ;; need to switch to cooked mode
  151.   (clear-input (controller-stream controller))
  152.   t
  153. )
  154.  
  155. (defmethod generic-stream-write-char ((controller screen-controller) ch)
  156.   (write-char ch (controller-stream controller))
  157. )
  158.  
  159. ;; for speed only
  160. (defmethod generic-stream-write-string ((controller screen-controller) string start len)
  161.   (write-string (substring string start (+ start len))
  162.                 (controller-stream controller)
  163. ) )
  164.  
  165. (defmethod generic-stream-finish-output ((controller screen-controller))
  166.   (finish-output (controller-stream controller))
  167. )
  168.  
  169. (defmethod generic-stream-force-output ((controller screen-controller))
  170.   (force-output (controller-stream controller))
  171. )
  172.  
  173. (defmethod generic-stream-clear-output ((controller screen-controller))
  174.   (clear-output (controller-stream controller))
  175. )
  176.  
  177. (defmethod generic-stream-close ((controller screen-controller))
  178.   (raw-mode (controller-stream controller) nil)
  179.   T
  180. )
  181. (defmethod generic-stream-close ((controller window-controller))
  182.   ; Don't need to call raw-mode on this window since it will go away anyway.
  183.   (close (controller-stream controller))
  184. )
  185.  
  186. ; returns a list of all characters immediately available on stream
  187. (defun stream-chars (stream)
  188.   (let ((res '()))
  189.     (loop
  190.       (let ((c (read-char-no-hang stream)))
  191.         (unless c (return))
  192.         (push c res)
  193.     ) )
  194.     (nreverse res)
  195. ) )
  196.  
  197. ; Parse an ANSI Control String:
  198. ; { #\CSI | #\ESC #\[ } { digits #\; }* [ digits [ #\; ] ] { rest }
  199. ; Return (rest . ... num2 num1)
  200. (defun parse-csi (string)
  201.   (let ((res '())
  202.         num
  203.         (start (cond ((eq (aref string 0) #\CSI)      1)
  204.                      ((and (eq (aref string 0) #\ESC)
  205.                            (> (length string) 1)
  206.                            (eq (aref string 1) #\[))  2)
  207.                      (t (error "Not a CSI sequence: ~S" string))
  208.        ))      )
  209.     (loop
  210.       (multiple-value-setq (num start) (parse-integer string :start start :junk-allowed t))
  211.       (when (null num) (return))
  212.       (push num res)
  213.       (when (and (< start (length string)) (eq (aref string start) #\;))
  214.         (incf start)                 ; skip ANSI separator
  215.     ) )
  216.     (cons (subseq string start) res) ; push rest
  217. ) )
  218.  
  219. ; Send a CSI sequence to the terminal and read the response, an ANSI sequence.
  220. ; Return a reversed list of numbers.
  221. ; (Note: As a side effect, a (clear-input stream) is done, which throws away
  222. ; characters.)
  223. (defun read-csi-response (stream send expected)
  224.   (clear-input stream)
  225.   (write-string send stream)
  226.   (let* ((chars
  227.            (or (stream-chars stream)
  228.                (error "Got no response from ~S." stream)
  229.          ) )
  230.          (response (parse-csi (coerce chars 'string))))
  231.     (unless (string= expected (first response))
  232.       (error (DEUTSCH "Von ~S schlechte Antwort erhalten: ~S"
  233.               ENGLISH "Got bad response from ~S: ~S"
  234.               FRANCAIS "Mauvaise rΘponse par ~S : ~S")
  235.              stream chars
  236.     ) )
  237.     (cdr response)
  238. ) )
  239.  
  240. (defun window-size (stream)
  241.   "Reports window size.
  242. Will flush pending characters!"
  243.   ;; (window-checks stream)
  244.   (when (and (generic-stream-p stream)
  245.              (typep (generic-stream-controller stream) 'screen-controller))
  246.     (raw-mode stream t)
  247.     (setq stream (controller-stream (generic-stream-controller stream)))
  248.   )
  249.   (let ((response
  250.           (read-csi-response
  251.             stream
  252.             (load-time-value (coerce '(#\CSI #\0 #\Space #\q) 'string))
  253.             "r"               ; parse-integer ate the space
  254.        )) )
  255.     (let ((width (first response))
  256.           (height (second response)))
  257.       ; Decrement width to avoid problems with wrapping/scrolling of the last line.
  258.       (values height (- width 1))
  259. ) ) )
  260.  
  261. (defun window-cursor-position (stream)
  262.   "Reports cursor position, report origin as 0;0.
  263. Will flush pending characters!"
  264.   ;; (window-checks stream)
  265.   (when (and (generic-stream-p stream)
  266.              (typep (generic-stream-controller stream) 'screen-controller))
  267.     (raw-mode stream t)
  268.     (setq stream (controller-stream (generic-stream-controller stream)))
  269.   )
  270.   (let ((response
  271.           (read-csi-response
  272.             stream
  273.             (load-time-value (coerce '(#\CSI #\6 #\n) 'string))
  274.             "R"
  275.        )) )
  276.     (values (1- (second response)) (1- (first response))) ; line;column
  277. ) )
  278.  
  279. (defun set-window-cursor-position (stream line column)
  280.   ;; ANSI position origin is 1;1, but SCREEN uses 0;0
  281.   (format stream "~a~d;~dH" #\CSI (1+ line) (1+ column))
  282.   (values)
  283. )
  284.  
  285. (defun clear-window (stream)
  286.   (write-char '#\FF stream)
  287.   (values)
  288. )
  289.  
  290. (defun clear-window-to-eot (stream)
  291.   (write-string (load-time-value (coerce '(#\CSI #\J) 'string)) stream)
  292.   (values)
  293. )
  294.  
  295. (defun clear-window-to-eol (stream)
  296.   (write-string (load-time-value (coerce '(#\CSI #\K) 'string)) stream)
  297.   (values)
  298. )
  299.  
  300. (defun delete-window-line (stream)
  301.   (write-string (load-time-value (coerce '(#\CSI #\M) 'string)) stream)
  302.   (values)
  303. )
  304.  
  305. (defun insert-window-line (stream)
  306.   (write-string (load-time-value (coerce '(#\CSI #\L) 'string)) stream)
  307.   (values)
  308. )
  309.  
  310. (defun highlight-on (stream)
  311.   (write-string (load-time-value (coerce '(#\CSI #\1 #\m) 'string)) stream)
  312.   (values)
  313. )
  314.  
  315. (defun highlight-off (stream)
  316.   (write-string (load-time-value (coerce '(#\CSI #\m) 'string)) stream)
  317.   (values)
  318. )
  319.  
  320. (defun window-cursor-on (stream)
  321.   (write-string (load-time-value (coerce '(#\CSI #\Space #\p) 'string)) stream)
  322.   (values)
  323. )
  324.  
  325. (defun window-cursor-off (stream)
  326.   (write-string (load-time-value (coerce '(#\CSI #\0 #\Space #\p) 'string)) stream)
  327.   (values)
  328. )
  329.  
  330.  
  331. ;; Read characters in raw mode
  332. (defun read-raw-char (stream)
  333.   (raw-mode stream t)
  334.   (setq stream (controller-stream (generic-stream-controller stream)))
  335.   (read-char stream)
  336. )
  337.  
  338. ;; This function does a simple mapping from CSI-sequences as reported
  339. ;; by the Amiga keyboard to characters with HYPER (even SUPER or CONTROL) bit
  340. ;; set.
  341. ;; key   codes  shift   character
  342. ;; f1    CSI0~  CSI10~  #\f1, #\s-f1
  343. ;; f10   CSI9~  CSI19~  #\f10, #\s-f10
  344. ;; Help  CSI?~  CSI?~   #\Help
  345. ;; Up    CSIA   CSIT    #\Up,    #\S-Up
  346. ;; Down  CSIB   CSIS    #\Down,  #\S-Down
  347. ;; Left  CSID   CSI A   #\Left,  #\S-Left
  348. ;; Right CSIC   CSI @   #\Right, #\S-Right
  349. (defun read-keyboard-char (stream)
  350.   (let ((c (read-raw-char stream)))
  351.     (if (char= c '#\CSI)
  352.       (let ((chars '()) c)
  353.         (loop
  354.           (setq c (read-raw-char stream))
  355.           (unless (char<= #\Space c #\?) (return))
  356.           (push c chars)
  357.         )
  358.         (cond ((char/= c '#\~) ; arrow keys
  359.                (or (cdr (assoc c (if chars
  360.                                    '((#\A . #\S-Left)
  361.                                      (#\@ . #\S-Right)
  362.                                     )
  363.                                    '((#\A . #\Up)
  364.                                      (#\B . #\Down)
  365.                                      (#\C . #\Right)
  366.                                      (#\D . #\Left)
  367.                                      (#\S . #\S-Down)
  368.                                      (#\T . #\S-Up)
  369.                                     )
  370.                    )    )        )
  371.                    '#\CSI
  372.               ))
  373.               ((null chars) '#\CSI) ; don't parse this...
  374.               ((eq (first chars) '#\?) '#\Help) ; Help key
  375.               ((not (digit-char-p (first chars))) '#\CSI) ; don't parse this...
  376.               ((null (rest chars)) ; f1 ... f10
  377.                (int-char (+ (char-int '#\f1) (digit-char-p (first chars))))
  378.               )
  379.               ((eq '#\1 (second chars)) ; F1 ... F10
  380.                (int-char (+ (char-int '#\s-f1) (digit-char-p (first chars))))
  381.               )
  382.               (t '#\CSI) ; don't parse this...
  383.       ) )
  384.       (if (and (<= 1 (char-int c) 26) ; Ctrl-A ... Ctrl-Z
  385.                (not (or (eql c #\Newline) (eql c #\Backspace) (eql c #\Tab)
  386.                         (eql c #\Return)
  387.           )    )    )
  388.         (set-char-bit (int-char (+ 64 (char-int c))) :CONTROL t)
  389.         c
  390. ) ) ) )
  391.  
  392.  
  393. ;; This is another class of generic streams. A KEYBOARD stream is just
  394. ;; a wrapper around another stream, just like the ALIAS streams in gstream.lsp,
  395. ;; except that the read-char method calls the read-keyboard-char function.
  396. ;; The mode is switched to raw when the stream is created and switched back
  397. ;; when the stream is closed.
  398. (defclass keyboard-controller (generic-stream-controller)
  399.   ((orig-stream :initarg :orig-stream)
  400.    (orig-mode :initarg :orig-mode)
  401. ) )
  402. (defun make-keyboard-stream (orig-stream)
  403.   (make-generic-stream
  404.     (make-instance 'keyboard-controller :orig-stream orig-stream
  405.                                         :orig-mode (raw-mode orig-stream t)
  406. ) ) )
  407. (defmethod generic-stream-read-char ((controller keyboard-controller))
  408.   (with-slots (orig-stream) controller
  409.     (read-keyboard-char orig-stream)
  410. ) )
  411. (defmethod generic-stream-listen ((controller keyboard-controller))
  412.   (with-slots (orig-stream) controller
  413.     (raw-mode orig-stream t)
  414.     (if (listen orig-stream)
  415.       0 ; something available
  416.       (let ((ch (read-char-no-hang orig-stream nil t)))
  417.         (cond ((eql ch t) -1) ; eof
  418.               ((null ch) +1) ; nothing available, not EOF
  419.               (t (unread-char ch orig-stream) 0) ; something available
  420. ) ) ) ) )
  421. (defmethod generic-stream-clear-input ((controller keyboard-controller))
  422.   (with-slots (orig-stream) controller
  423.     (raw-mode orig-stream t)
  424.     (clear-input orig-stream)
  425.     t
  426. ) )
  427. (defmethod generic-stream-write-char ((controller keyboard-controller) ch)
  428.   (with-slots (orig-stream) controller
  429.     (write-char ch orig-stream)
  430. ) )
  431. (defmethod generic-stream-finish-output ((controller keyboard-controller))
  432.   (with-slots (orig-stream) controller
  433.     (finish-output orig-stream)
  434. ) )
  435. (defmethod generic-stream-force-output ((controller keyboard-controller))
  436.   (with-slots (orig-stream) controller
  437.     (force-output orig-stream)
  438. ) )
  439. (defmethod generic-stream-clear-output ((controller keyboard-controller))
  440.   (with-slots (orig-stream) controller
  441.     (clear-output orig-stream)
  442. ) )
  443. (defmethod generic-stream-read-byte ((controller keyboard-controller))
  444.   (with-slots (orig-stream) controller
  445.     (raw-mode orig-stream t)
  446.     (read-byte orig-stream nil nil)
  447. ) )
  448. (defmethod generic-stream-write-byte (i (controller keyboard-controller))
  449.   (with-slots (orig-stream) controller
  450.     (write-byte i orig-stream)
  451. ) )
  452. (defmethod generic-stream-close ((controller keyboard-controller))
  453.   (with-slots (orig-stream orig-mode) controller
  454.     (raw-mode orig-stream orig-mode)
  455.   )
  456.   ; don't close orig-stream
  457. )
  458.  
  459. (defmethod generic-raw-mode ((controller keyboard-controller) mode)
  460.   (with-slots (orig-stream) controller
  461.     (raw-mode orig-stream mode)
  462. ) )
  463.  
  464.  
  465. #|
  466. ;; Now we have all pieces that make up the stream *keyboard-input* :
  467. ;; The (make-window nil) stream switches modes automatically, and
  468. ;; (make-keyboard-stream ...) adds the read-keyboard-char translation.
  469. (defparameter *keyboard-input* (make-keyboard-stream (make-window nil)))
  470. ;; Beware! (make-keyboard-stream ...) switches the mode at stream creation time!
  471. |#
  472.  
  473. ) ; #+AMIGA
  474.  
  475.  
  476. (defmacro with-window (&body body)
  477.   `(LET ((*WINDOW* (MAKE-WINDOW)))
  478.      (UNWIND-PROTECT (PROGN ,@body) (CLOSE *WINDOW*))
  479.    )
  480. )
  481.  
  482.